package org.cocopapaya.springws.ext.xml;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;

import org.springframework.core.io.Resource;
import org.springframework.xml.validation.SchemaLoaderUtils;
import org.springframework.xml.validation.ValidationErrorHandler;
import org.springframework.xml.validation.XmlValidationException;
import org.springframework.xml.validation.XmlValidator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class JaxpValidatorFactory {

    static XmlValidator createValidator(Resource[] resources, String schemaLanguage) throws IOException {
        try {
            Schema schema = CustomSchemaLoaderUtils.loadSchema(resources, schemaLanguage);
            return new Jaxp13Validator(schema);
        }
        catch (SAXException ex) {
            throw new XmlValidationException("Could not create Schema: " + ex.getMessage(), ex);
        }
    }

    private static class Jaxp13Validator implements XmlValidator {

        private Schema schema;

        public Jaxp13Validator(Schema schema) {
            this.schema = schema;
        }

        public SAXParseException[] validate(Source source) throws IOException {
            return validate(source, null);
        }

        public SAXParseException[] validate(Source source, ValidationErrorHandler errorHandler) throws IOException {
            if (errorHandler == null) {
                errorHandler = new DefaultValidationErrorHandler();
            }
            Validator validator = schema.newValidator();
            validator.setErrorHandler(errorHandler);
            try {
                validator.validate(source);
                return errorHandler.getErrors();
            }
            catch (SAXException ex) {
                throw new XmlValidationException("Could not validate source: " + ex.getMessage(), ex);
            }
        }
    }

    /** <code>ErrorHandler</code> implementation that stores errors and fatal errors in a list. */
    private static class DefaultValidationErrorHandler implements ValidationErrorHandler {

        private List<SAXParseException> errors = new ArrayList<SAXParseException>();

        public SAXParseException[] getErrors() {
            return errors.toArray(new SAXParseException[errors.size()]);
        }

        public void warning(SAXParseException ex) throws SAXException {
        }

        public void error(SAXParseException ex) throws SAXException {
            errors.add(ex);
        }

        public void fatalError(SAXParseException ex) throws SAXException {
            errors.add(ex);
        }
    }
}
